﻿using System;
using System.Collections.Generic;
using System.Transactions;
using Inventione.Framework.BusinessLogic;
using Inventione.Framework.DataAccess;
using Inventione.Outsourcing.DataAccess;
using Inventione.Outsourcing.DataLinq;
using Inventione.Outsourcing.BusinessLogic.Exceptions;
using Inventione.Framework.DataAccess.Exceptions;
using Inventione.Framework.BusinessLogic.Exceptions;

namespace Inventione.Outsourcing.BusinessLogic
{
    /// <summary>
    /// Classe de negáocio de Ocupação
    /// </summary>
    public class BLOcupacao : BLBase
    {
        // Representa o objeto de acesso a dados
        DAOcupacao da = null;

        /// <summary>
        /// Construtor padrão
        /// </summary>
        public BLOcupacao() : base("DEFAULT")
        {
            da = new DAOcupacao("DEFAULT");
        }

        /// <summary>
        /// Construtor utilizando o cliente
        /// </summary>
        /// <param name="tenantId">Identificador do cliente</param>
        public BLOcupacao(string tenantId)
            :base(tenantId)
        {
            da = new DAOcupacao(tenantId);
        }

        /// <summary>
        /// Obtem uma ocupação
        /// </summary>
        /// <param name="cdCargo">Código do cargo da ocupação</param>
        /// <param name="cdFuncionario">Código do funcionário ocupado</param>
        /// <param name="dtOcupacao">Data da ocupação</param>
        /// <returns>Entidade ocupação</returns>
        public OCUPACAO obterOcupacao(int cdCargo, int cdFuncionario, DateTime dtOcupacao)
        {
            OCUPACAO retorno = null;

            try
            {
                retorno = this.da.obterOcupacao(cdCargo, cdFuncionario, dtOcupacao);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Obtem a ocupação do funcionário em uma data que deve está no período de ocupação
        /// </summary>
        /// <param name="cdFuncionario">Código do funcionário</param>
        /// <param name="data">Data base que deve está entre o período de ocupação</param>
        /// <returns>Uma ocupação do funcionário</returns>
        public OCUPACAO obterOcupacaoFuncionario(int cdFuncionario, DateTime data)
        {
            OCUPACAO retorno = null;

            try
            {
                retorno = this.da.obterOcupacaoFuncionario(cdFuncionario, data);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Obtem a ocupação do funcionário em uma data que deve está no período de ocupação,
        /// considerando que a ocupação deve está em vigência
        /// </summary>
        /// <param name="cdFuncionario">Código do funcionário</param>
        /// <param name="data">Data base que deve está entre o período de ocupação</param>
        /// <returns>Uma ocupação do funcionário</returns>
        public OCUPACAO obterOcupacaoVigenteFuncionario(int cdFuncionario, DateTime data)
        {
            OCUPACAO retorno = null;

            try
            {
                retorno = this.da.obterOcupacaoVigenteFuncionario(cdFuncionario, data);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Valida se um funcionário está ou estava ocupado em uma determinada data
        /// </summary>
        /// <param name="cdFuncionario">Código do funcionário</param>
        /// <param name="data">Data base que deve está entreo o período de ocupação</param>
        /// <returns>Se há ocupação (verdadeiro ou falso)</returns>
        public bool existeOcupacaoFuncionario(int cdFuncionario, DateTime data)
        {
            bool retorno = false;

            try
            {
                retorno = this.da.existeOcupacaoFuncionario(cdFuncionario, data);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Valida se um há uma ocupação em aberto para o funcionário, podendo ser uma ocupação futura
        /// </summary>
        /// <param name="cdFuncionario">Código do funcionário</param>        
        /// <returns>Se há ocupação (verdadeiro ou falso)</returns>
        public bool existeOcupacaoFuncionario(int cdFuncionario)
        {
            bool retorno = false;

            try
            {
                retorno = this.da.existeOcupacaoFuncionario(cdFuncionario);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Lista os funcionários ocupados no contrato na data informada
        /// </summary>
        /// <param name="cdContrato">Código do contrato</param>
        /// <param name="dtOcupacao">Data base para validar ocupação</param>
        /// <returns>Lista de funcionários para combo</returns>
        public List<TOCombo> listarFuncionariosOcupados(int cdContrato, DateTime dtOcupacao)
        {
            List<TOCombo> retorno = null;

            try
            {
                retorno = this.da.listarFuncionariosOcupados(cdContrato, dtOcupacao);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Valida se um funcionário está ocupado em uma data
        /// </summary>
        /// <param name="cdFuncionario">Código do funcionário</param>
        /// <param name="dtBase">Data base da ocupação</param>
        /// <returns>Se está ocupado (true / false)</returns>
        public bool validarFuncionarioOcupadoNaData(int cdFuncionario, DateTime dtBase)
        {
            bool retorno = false;

            try
            {
                retorno = this.da.validarFuncionarioOcupadoNaData(cdFuncionario, dtBase);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Valida se um funcionário está ocupado após uma data
        /// </summary>
        /// <param name="cdFuncionario">Código do funcionário</param>
        /// <param name="dtBase">Data base da ocupação</param>
        /// <returns>Se está ocupado (true / false)</returns>
        public bool validarFuncionarioOcupadoAposData(int cdFuncionario, DateTime dtBase)
        {
            bool retorno = false;

            try
            {
                retorno = this.da.validarFuncionarioOcupadoAposData(cdFuncionario, dtBase);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Valida se uma ocupação foi substituída no momento da saída
        /// </summary>
        /// <param name="eOcupacao">entidade ocupação</param>
        /// <returns>Indica se foi substituída (true / false)</returns>
        public bool validarOcupacaoSubstituida(OCUPACAO eOcupacao)
        {
            bool retorno = false;

            try
            {
                retorno = this.da.validarOcupacaoSubstituida(eOcupacao);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Cancela uma substituição de uma ocupação
        /// </summary>
        /// <param name="eOcupacao">Entidade ocupaçao</param>
        public void cancelarSubstituicaoOcupacao(OCUPACAO eOcupacao)
        {
            try
            {
                this.da.cancelarSubstituicaoOcupacao(eOcupacao);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }
        }

        /// <summary>
        /// Ocupa um funcionário
        /// </summary>
        /// <param name="entity">Ocupação</param>
        /// <param name="txMotivo">Motivo da desocupação, caso outro funcionário esteja sendo substituido</param>
        /// <param name="excluirOcorrenciaFuturasDoFuncionarioSubstituido">Indica se é para excluir as ocorrências futuras do funcionário substituído</param>
        public void ocuparFuncionario(OCUPACAO eOcupacao, int? cdMotivoSubstituido, string txObservacao,
            bool excluirOcorrenciaFuturasDoFuncionarioSubstituido, bool cancelarMedicoesContendoAOcupacao, string nmAutor)
        {
            try
            {
                // Valida se o funcionário possui uma ocupação após o registro atual, fazendo com que tal registro
                if ((!eOcupacao.OCUP_DT_SAIDA.HasValue || !eOcupacao.MODE_CD_MOTIVO_DESOCUPACAO.HasValue) &&
                    this.validarFuncionarioOcupadoAposData(eOcupacao.FUNC_CD_FUNCIONARIO, eOcupacao.OCUP_DT_OCUPACAO))
                {
                    throw new BLException(@"O funcionário possui ocupação posterior a este registro de ocupação. 
                                            Assim, só é possível registrar ocupações históricas, contendo data e motivo de saída.");
                }

                // Valida a obrigatoriedade para data de saída e motivo de saída, se um ocorrer o outro deve ocorrer também                
                if ((eOcupacao.OCUP_DT_SAIDA.HasValue && !eOcupacao.MODE_CD_MOTIVO_DESOCUPACAO.HasValue) ||
                    (!eOcupacao.OCUP_DT_SAIDA.HasValue && eOcupacao.MODE_CD_MOTIVO_DESOCUPACAO.HasValue))
                {
                    throw new BLException(@"Se informar a data de saída o motivo também deve ser informado.");
                }

                // Validando se o funcionário está ocupado
                if (this.validarFuncionarioOcupadoNaData(eOcupacao.FUNC_CD_FUNCIONARIO, eOcupacao.OCUP_DT_OCUPACAO))
                {
                    throw new BLException(@"Funcionário já está ocupado na data de ocupação informada.");
                }
                if (eOcupacao.OCUP_DT_SAIDA.HasValue && this.validarFuncionarioOcupadoNaData(eOcupacao.FUNC_CD_FUNCIONARIO, eOcupacao.OCUP_DT_SAIDA.Value))
                {
                    throw new BLException(@"Funcionário já está ocupado na data de saída informada.");
                }

                BLMedicao blMedicao = new BLMedicao(base.TenantId);
                BLContrato blContrato = new BLContrato(base.TenantId);

                if (!blContrato.validarVigencia(eOcupacao.LOCO_CD_CONTRATO, eOcupacao.OCUP_DT_OCUPACAO))
                {
                    throw new BLException(@"Não é possível ocupar um funcionário em uma data fora da vigência do contrato.");
                }

                bool existeMedicaoNaoAprovadaNaData = false;
                bool existeMedicaoNaoAprovadaAposData = false;
                bool existeMedicaoNaoAprovadaNoPeriodo = false;

                // Valida se há uma medição na data
                if (eOcupacao.OCUP_DT_SAIDA.HasValue)
                {
                    if (blMedicao.existeMedicaoNoPeriodo(eOcupacao.LOCO_CD_CONTRATO, eOcupacao.OCUP_DT_OCUPACAO, eOcupacao.OCUP_DT_SAIDA.Value, "A", true))
                    {
                        throw new BLException(@"Não é possível ocupar um funcionário quando há medição aprovada no período.");
                    }
                    else
                    {
                        existeMedicaoNaoAprovadaNoPeriodo = blMedicao.existeMedicaoNoPeriodo(eOcupacao.LOCO_CD_CONTRATO, eOcupacao.OCUP_DT_OCUPACAO, eOcupacao.OCUP_DT_SAIDA.Value, "C", false);

                        if (existeMedicaoNaoAprovadaNoPeriodo && !cancelarMedicoesContendoAOcupacao)
                        {
                            throw new BLException(@"Não é possível ocupar um funcionário quando há medição no período.
                                                    Para continuar é necessário cancelar a(s) medição(ões) existente(s).");
                        }
                    }
                }
                else
                {
                    // Valida se há uma medição na data de ocupação e após
                    if (blMedicao.existeMedicaoNaData(eOcupacao.LOCO_CD_CONTRATO, eOcupacao.OCUP_DT_OCUPACAO, "A", true) ||
                        blMedicao.existeMedicaoAposData(eOcupacao.LOCO_CD_CONTRATO, eOcupacao.OCUP_DT_OCUPACAO, "A", true))
                    {
                        throw new BLException(@"Não é possível ocupar um funcionário quando há medição aprovada no período.");
                    }
                    else
                    {
                        existeMedicaoNaoAprovadaNaData = blMedicao.existeMedicaoNaData(eOcupacao.LOCO_CD_CONTRATO, eOcupacao.OCUP_DT_OCUPACAO, "C", false);
                        existeMedicaoNaoAprovadaAposData = blMedicao.existeMedicaoAposData(eOcupacao.LOCO_CD_CONTRATO, eOcupacao.OCUP_DT_OCUPACAO, "C", false);

                        if ((existeMedicaoNaoAprovadaNaData || existeMedicaoNaoAprovadaAposData) && !cancelarMedicoesContendoAOcupacao)
                        {
                            throw new BLException(@"Não é possível ocupar um funcionário quando há medição no período.
                                    Para continuar é necessário cancelar a(s) medição(ões) existente(s).");
                        }
                    }
                }

                // Escopo transacional para determinar a dependência das ocorrências
                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        // Desocupa o funcionário que está sendo substituido
                        if (eOcupacao.FUNC_CD_FUNCIONARIO_SUBSTITUIDO.HasValue)
                        {
                            // Obtem a ocupação indicada a ser substituida
                            OCUPACAO ocupacaoSubstituida = this.obterOcupacaoVigenteFuncionario(eOcupacao.FUNC_CD_FUNCIONARIO_SUBSTITUIDO.Value, eOcupacao.OCUP_DT_OCUPACAO);

                            if (ocupacaoSubstituida != null)
                            {
                                // Seta os valores da ocupação substituida na nova ocupação
                                eOcupacao.FUNC_CD_FUNCIONARIO_SUBSTITUIDO = ocupacaoSubstituida.FUNC_CD_FUNCIONARIO;
                                eOcupacao.CARG_CD_CARGO_SUBSTITUIDO = ocupacaoSubstituida.CARG_CD_CARGO;
                                eOcupacao.OCUP_DT_OCUPACAO_SUBSTITUIDO = ocupacaoSubstituida.OCUP_DT_OCUPACAO;

                                ocupacaoSubstituida.MODE_CD_MOTIVO_DESOCUPACAO = cdMotivoSubstituido.Value;
                                ocupacaoSubstituida.OCUP_TX_OBSERVACAO = txObservacao;


                                // Desocupa o funcionário no dia anterior a nova ocupação
                                this.desocuparFuncionario(ocupacaoSubstituida.FUNC_CD_FUNCIONARIO, eOcupacao.OCUP_DT_OCUPACAO.AddDays(-1), cdMotivoSubstituido.Value,
                                    txObservacao, excluirOcorrenciaFuturasDoFuncionarioSubstituido, cancelarMedicoesContendoAOcupacao, nmAutor);
                            }
                            else
                            {
                                throw new BLException(@"A ocupação informada para substituição não foi encontrada.");
                            }
                        }


                        if (cancelarMedicoesContendoAOcupacao &&
                            (existeMedicaoNaoAprovadaNaData || existeMedicaoNaoAprovadaAposData || existeMedicaoNaoAprovadaNoPeriodo))
                        {
                            if (eOcupacao.OCUP_DT_SAIDA.HasValue && existeMedicaoNaoAprovadaNoPeriodo)
                            {
                                // Cancela medições no período da ocupação
                                blMedicao.cancelarMedicoesNoPeriodo(eOcupacao.LOCO_CD_CONTRATO, eOcupacao.OCUP_DT_OCUPACAO, eOcupacao.OCUP_DT_SAIDA.Value, 2, nmAutor);
                            }
                            else
                            {
                                // Cancela as medições que envolvem a ocupação, desde que não estaja aprovada ou já cancelada
                                blMedicao.cancelarMedicoesAPartirDaData(eOcupacao.LOCO_CD_CONTRATO, eOcupacao.OCUP_DT_OCUPACAO, 2, nmAutor);
                            }
                        }

                        // Ocupa o funcionário
                        this.da.ocuparFuncionario(eOcupacao);

                        // Completa a transação
                        scope.Complete();
                    }
                    catch (Exception ex)
                    {
                        scope.Dispose();

                        throw ex;
                    }
                }
            }
            catch (BLException blEx)
            {
                throw blEx;
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }
        }

        /// <summary>
        /// Desocupa um funcionário de um cargo
        /// </summary>
        /// <param name="cdFuncionario">Código do funcionário a ser desocupado</param>
        /// <param name="dsMotivo">Motivo da desocupação</param>
        /// <param name="dtDesocupacao">Data da desocupação, normalmente é a data de outra ocupação</param>
        public void desocuparFuncionario(int cdFuncionario, DateTime dtDesocupacao, int cdMotivoDesocupacao, string txObservacao,
            bool excluirOcorrenciaFuturas, bool cancelarMedicoesContendoAOcupacao, string nmAutor)
        {
            try
            {
                // Obtendo a ocupação a ser desocupada
                OCUPACAO eOcupacaoADesocupar = this.obterOcupacaoVigenteFuncionario(cdFuncionario, dtDesocupacao);
                if (eOcupacaoADesocupar == null)
                {
                    throw new BLException(@"Não foi possível desocupar o funcionário pois a ocupação não foi encontrada. Verifique
                                            se a data informada para desocupação é maior que a data de ocupação.");
                }

                // Gerenciadores de negócio
                BLMedicao blMedicao = new BLMedicao(base.TenantId);
                BLOcorrencia blOcorrencia = new BLOcorrencia(base.TenantId);

                bool existeMedicaoNaoAprovadaNaData;
                bool existeMedicaoNaoAprovadaAposData;

                // Valida se há uma medição Aprovada na data de desocupacão
                if (blMedicao.existeMedicaoNaData(eOcupacaoADesocupar.LOCO_CD_CONTRATO, dtDesocupacao, "A", true))
                {
                    throw new BLException(@"Não é possível desocupar um funcionário quando há medição aprovada na data de saída.");
                }
                else
                {
                    existeMedicaoNaoAprovadaNaData = blMedicao.existeMedicaoNaData(eOcupacaoADesocupar.LOCO_CD_CONTRATO, dtDesocupacao, "C", false);

                    if (existeMedicaoNaoAprovadaNaData && !cancelarMedicoesContendoAOcupacao)
                    {
                        throw new BLException(@"Não é possível desocupar um funcionário quando há medição na data de saída.
                                                Para continuar é necessário cancelar a(s) medição(ões) existente(s).");
                    }
                }

                // Valida se há uma medição Aprovada na data de desocupacão
                if (blMedicao.existeMedicaoAposData(eOcupacaoADesocupar.LOCO_CD_CONTRATO, dtDesocupacao, "A", true))
                {
                    throw new BLException(@"Não é possível desocupar um funcionário quando há medição aprovada após a data de saída.");
                }
                else
                {
                    existeMedicaoNaoAprovadaAposData = blMedicao.existeMedicaoAposData(eOcupacaoADesocupar.LOCO_CD_CONTRATO, dtDesocupacao, "C", false);

                    if (existeMedicaoNaoAprovadaAposData && !cancelarMedicoesContendoAOcupacao)
                    {
                        throw new BLException(@"Não é possível desocupar um funcionário quando há medição após a data de saída.
                                                Para continuar é necessário cancelar a(s) medição(ões) existente(s).");
                    }
                }

                bool existeOcorrenciaAposDataDesocupacao = blOcorrencia.existeOcorrenciasAposData(eOcupacaoADesocupar, dtDesocupacao);

                // Valida se existe ocorrência após a data de desocupação, pois estas deverão ser canceladas
                if (existeOcorrenciaAposDataDesocupacao && !excluirOcorrenciaFuturas)
                {
                    throw new BLException(@"Não é possível desocupar um funcionário que possui ocorrências futuras. 
                                            Se necessário cancele (exclua) tais ocorrências.");
                }

                // Contexto transacional
                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        if (existeOcorrenciaAposDataDesocupacao && excluirOcorrenciaFuturas)
                        {
                            // Exlcui ocorrências após a data de desocupação
                            blOcorrencia.excluirOcorrenciasAposData(eOcupacaoADesocupar, dtDesocupacao);
                        }
                        if (cancelarMedicoesContendoAOcupacao && (existeMedicaoNaoAprovadaNaData || existeMedicaoNaoAprovadaAposData))
                        {
                            // Cancela as medições que envolvem a ocupação, desde que não estaja aprovada ou já cancelada
                            blMedicao.cancelarMedicoesAPartirDaData(eOcupacaoADesocupar.LOCO_CD_CONTRATO, dtDesocupacao, 2, nmAutor);
                        }

                        // Registra a desocupação
                        this.da.desocuparFuncionario(eOcupacaoADesocupar, dtDesocupacao, cdMotivoDesocupacao, txObservacao);

                        scope.Complete();
                    }
                    catch (Exception ex)
                    {
                        scope.Dispose();

                        throw ex;
                    }
                }
            }
            catch (BLException blex)
            {
                throw blex;
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }
        }

        /// <summary>
        /// Cancela (exclui) uma ocupação ainda não utilizada
        /// </summary>
        /// <param name="entity">Entidade ocupação</param>
        public void cancelarOcupacao(OCUPACAO entity)
        {
            try
            {
                // Não é possível cancelar uma ocupação que já foi desocupada
                if (entity.OCUP_DT_SAIDA.HasValue)
                {
                    throw new BLException(@"Não é possível cancelar uma ocupação que foi registrada a desocupação.");
                }

                BLMedicao blMedicao = new BLMedicao(base.TenantId);

                // Valida se há uma medição na data, não considera as canceladas
                if (blMedicao.existeMedicaoNaData(entity.LOCO_CD_CONTRATO, entity.OCUP_DT_OCUPACAO, "C", false))
                {
                    throw new BLException(@"Não é possível cancelar uma ocupação quando há medição que compreende a data de ocupação.");
                }

                BLOcorrencia blOcorrencia = new BLOcorrencia(base.TenantId);

                // Valida se uma ocupação possui ocorrências após uma data que estão envolvidas em medição
                if (blOcorrencia.validarOcorrenciasAposDataPossuiMedicaoValida(entity, entity.OCUP_DT_OCUPACAO))
                {
                    throw new BLException("A ocupação não pode ser cancelada, há ocorrências já vinculadas a uma medição válida.");
                }

                throw new InvalidOperationException("Inserir validação da existência de medição e informar que deve ser cancelada");

                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        // Exlcui ocorrências após a data de ocupação, ou seja, todas
                        blOcorrencia.excluirOcorrenciasAposData(entity, entity.OCUP_DT_OCUPACAO);

                        // Registra o cancelamento
                        this.da.cancelarOcupacao(entity);

                        scope.Complete();
                    }
                    catch (Exception ex)
                    {
                        scope.Dispose();

                        throw ex;
                    }

                }
            }
            catch (BLException blex)
            {
                throw blex;
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }
        }

        /// <summary>
        /// Lista as ocupações para um cargo, válidas para um determinado período
        /// </summary>
        /// <param name="cdCargo">Código do cargo</param>
        /// <param name="dtInicial">Data inicial de referência a medição</param>
        /// <param name="dtFinal">Data final de referência a medição</param>
        /// <returns>Lista de ocupações</returns>
        public List<OCUPACAO> listarOcupacoesPorCargoPeriodo(int cdCargo, DateTime dtInicial, DateTime dtFinal)
        {
            List<OCUPACAO> retorno = null;

            try
            {
                retorno = this.da.listarOcupacoesPorCargoPeriodo(cdCargo, dtInicial, dtFinal);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Lista as ocupações de um contrato em determinado período
        /// </summary>
        /// <param name="cdContrato">Código do contrato</param>
        /// <param name="dtInicial">Data inicial do período base</param>
        /// <param name="dtFinal">Data final do período base</param>
        /// <returns>Lista de ocupações</returns>
        public List<OCUPACAO> listarOcupacoesPorContratoPeriodo(int cdContrato, DateTime dtInicial, DateTime dtFinal)
        {
            List<OCUPACAO> retorno = null;

            try
            {
                retorno = this.da.listarOcupacoesPorCargoPeriodo(cdContrato, dtInicial, dtFinal);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Consulta as ocupações de funcionários em cargos
        /// </summary>
        /// <param name="cdContrato">Código do contrato</param>
        /// <param name="cdCargo">Código do cargo</param>
        /// <param name="cdLocal">Código do local de contrato</param>
        /// <param name="cdFuncionario">Código do funcionário</param>
        /// <param name="maximumRows">Quantidade máxima de linhas de retorno</param>
        /// <param name="startRowIndex">Linha inicial de retorno</param>
        /// <param name="sortExpression">Compo para ordenação</param>
        /// <returns>Lista de ocupações</returns>
        public List<OCUPACAO> consultarOcupacoes(int cdContrato, int cdCargo, int cdLocal, int cdFuncionario, string nmFuncionario,
            int maximumRows, int startRowIndex, string sortExpression)
        {
            List<OCUPACAO> retorno = null;

            try
            {
                retorno = this.da.consultarOcupacoes(cdContrato, cdCargo, cdLocal, cdFuncionario, nmFuncionario, maximumRows, startRowIndex, sortExpression);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Consulta as ocupações de funcionários em cargos
        /// </summary>
        /// <param name="cdContrato">Código do contrato</param>
        /// <param name="cdCargo">Código do cargo</param>
        /// <param name="cdLocal">Código do local de contrato</param>
        /// <param name="cdFuncionario">Código do funcionário</param>
        /// <returns>Quantidade de registros atingidos pela consulta</returns>
        public int consultarOcupacoesCount(int cdContrato, int cdCargo, int cdLocal, int cdFuncionario, string nmFuncionario)
        {
            int retorno = 0;

            try
            {
                retorno = this.da.consultarOcupacoesCount(cdContrato, cdCargo, cdLocal, cdFuncionario, nmFuncionario);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Sumariza as ocupações de um contrato, de acordo com uma medição
        /// </summary>
        /// <param name="medicao">Entidade que representa a medição contratual</param>
        /// <returns>Entidade que representa a ocupação e a sumarização</returns>
        public List<TOQuantidadeOcupacao> sumarizarOcupacoesMedicaoContratual(MEDICAO medicao)
        {
            List<TOQuantidadeOcupacao> retorno = null;

            try
            {
                retorno = this.da.sumarizarOcupacoesMedicaoContratual(medicao);
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Atualiza uma ocupação
        /// </summary>
        /// <param name="entity">Entidade ocupação</param>
        public void atualizarOcupacao_OLD(OCUPACAO eOcupacao)
        {
            try
            {
                // Validando se o funcionário está ocupado em uma data
                if (this.validarFuncionarioOcupadoNaData(eOcupacao.FUNC_CD_FUNCIONARIO, eOcupacao.OCUP_DT_OCUPACAO))
                {
                    throw new BLException("Funcionário já está ocupado nesta data.");
                }

                // Valida se o funcionário possui uma ocupação após o registro atual, fazendo com que tal registro
                if ((!eOcupacao.OCUP_DT_SAIDA.HasValue || !eOcupacao.MODE_CD_MOTIVO_DESOCUPACAO.HasValue) &&
                    this.validarFuncionarioOcupadoAposData(eOcupacao.FUNC_CD_FUNCIONARIO, eOcupacao.OCUP_DT_OCUPACAO))
                {
                    throw new BLException(@"O funcionário possui ocupação posterior a este registro de ocupação. 
                                            Assim, só é possível atualizar ocupações históricas, contendo data e motivo de saída.");
                }

                // Valida a obrigatoriedade para data de saída e motivo de saída, se um ocorrer o outro deve ocorrer também                
                if ((eOcupacao.OCUP_DT_SAIDA.HasValue && !eOcupacao.MODE_CD_MOTIVO_DESOCUPACAO.HasValue) ||
                    (!eOcupacao.OCUP_DT_SAIDA.HasValue && eOcupacao.MODE_CD_MOTIVO_DESOCUPACAO.HasValue))
                {
                    throw new BLException(@"Se informar a data de saída o motivo também deve ser informado.");
                }


                // Obtendo a ocupação
                OCUPACAO eOcupacaoAnterior = this.obterOcupacao(eOcupacao.CARG_CD_CARGO, eOcupacao.FUNC_CD_FUNCIONARIO, eOcupacao.OCUP_DT_OCUPACAO);

                BLMedicao blMedicao = new BLMedicao(base.TenantId);
                BLOcorrencia blOcorrencia = new BLOcorrencia(base.TenantId);

                // Não é possível colocar data de desocupação, faz-se necessário a desocupação
                if (!eOcupacaoAnterior.OCUP_DT_SAIDA.HasValue && eOcupacao.OCUP_DT_SAIDA.HasValue)
                {
                    throw new BLException(@"Não é permitido desocupar um funcionário através de atualização de ocupação.
                                            Utilize a opção desocupar funcionário.");
                }

                // Se estiver sendo retirada a data de desocupação deverão serem validadas algumas regras
                if (eOcupacaoAnterior.OCUP_DT_SAIDA.HasValue && !eOcupacao.OCUP_DT_SAIDA.HasValue)
                {
                    // Valida se existe medição na data de saída anterior
                    if (blMedicao.existeMedicaoNaData(eOcupacaoAnterior.CARGO.CONT_CD_CONTRATO, eOcupacaoAnterior.OCUP_DT_SAIDA.Value, "C", false))
                    {
                        throw new BLException(@"Existe medição que compreende a data de desocupação anterior.");
                    }

                    // Redefine o motivo da desocupação
                    eOcupacao.OCUP_TX_OBSERVACAO = string.Empty;
                }
                else if (eOcupacaoAnterior.OCUP_DT_SAIDA.HasValue && eOcupacao.OCUP_DT_SAIDA.HasValue &&
                         eOcupacaoAnterior.OCUP_DT_SAIDA != eOcupacao.OCUP_DT_SAIDA)
                {
                    // Valida se existe medição na nova data de saída
                    if (blMedicao.existeMedicaoNaData(eOcupacaoAnterior.CARGO.CONT_CD_CONTRATO, eOcupacao.OCUP_DT_SAIDA.Value, "C", false))
                    {
                        throw new BLException(@"Existe medição que compreende a nova data de desocupação.");
                    }

                    // Se a nova data de desocupação for menor que a data anterior as ocorrências após devem ser excluídas 
                    if (eOcupacao.OCUP_DT_SAIDA < eOcupacaoAnterior.OCUP_DT_SAIDA)
                    {
                        blOcorrencia.excluirOcorrenciasAposData(eOcupacaoAnterior, eOcupacao.OCUP_DT_SAIDA.Value);
                    }
                }

                // Atualiza a ocupação
                this.da.atualizarOcupacao_OLD(eOcupacao);
            }
            catch (BLException blex)
            {
                throw blex;
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }
        }

        /// <summary>
        /// Reativa uma ocupação
        /// </summary>
        /// <param name="entity">Entidade ocupação</param>
        public void reativarOcupacao(OCUPACAO eOcupacao, bool cancelarMedicoesFuturasContendoAOcupacao, string nmAutor)
        {
            try
            {
                // Obtendo a ocupação
                OCUPACAO eOcupacaoAnterior = this.obterOcupacao(eOcupacao.CARG_CD_CARGO, eOcupacao.FUNC_CD_FUNCIONARIO, eOcupacao.OCUP_DT_OCUPACAO);

                if (eOcupacaoAnterior == null)
                {
                    throw new BLException("Ocupação não encontrada.");
                }

                // Valida se a ocupação está ativa
                if (!eOcupacaoAnterior.OCUP_DT_SAIDA.HasValue)
                {
                    throw new BLException("Ocupação já está ativa.");
                }

                // Valida se o funcionário possui uma ocupação após o registro atual, fazendo com que tal registro
                if (this.validarFuncionarioOcupadoAposData(eOcupacaoAnterior.FUNC_CD_FUNCIONARIO, eOcupacaoAnterior.OCUP_DT_SAIDA.Value))
                {
                    throw new BLException(@"O funcionário possui ocupação posterior a este registro de ocupação. 
                                            Assim, não é possível reativar a ocupação.");
                }

                BLMedicao blMedicao = new BLMedicao(base.TenantId);
                BLContrato blContrato = new BLContrato(base.TenantId);

                bool existeMedicaoNaoAprovadaAposData = false;

                // Valida se há uma medição aprovada após a data de saída
                if (blMedicao.existeMedicaoAposData(eOcupacaoAnterior.LOCO_CD_CONTRATO, eOcupacaoAnterior.OCUP_DT_SAIDA.Value, "A", true))
                {
                    throw new BLException(@"Não é possível reativar uma ocupação quando há medição aprovada no período.");
                }
                else
                {
                    // Valida se há uma medição não aprovada após a data de saída

                    existeMedicaoNaoAprovadaAposData = blMedicao.existeMedicaoAposData(eOcupacaoAnterior.LOCO_CD_CONTRATO, eOcupacaoAnterior.OCUP_DT_OCUPACAO, "C", false);

                    if (existeMedicaoNaoAprovadaAposData && !cancelarMedicoesFuturasContendoAOcupacao)
                    {
                        throw new BLObrigatorioCancelarMedicoesFuturasException(@"Não é possível reativar uma ocupação quando há medição no período.
                                    Para continuar é necessário cancelar a(s) medição(ões) existente(s).");
                    }
                }

                // Escopo transacional para determinar a dependência das ocorrências
                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        if (existeMedicaoNaoAprovadaAposData)
                        {
                            // Cancela as medições que envolvem a ocupação, desde que não estaja aprovada ou já cancelada
                            blMedicao.cancelarMedicoesAPartirDaData(eOcupacaoAnterior.LOCO_CD_CONTRATO, eOcupacaoAnterior.OCUP_DT_SAIDA.Value,
                                2, nmAutor);
                        }

                        // Valida se uma ocupação foi substituída no momento da saída
                        if (validarOcupacaoSubstituida(eOcupacao))
                        {
                            // Cancela a substituição
                            this.cancelarSubstituicaoOcupacao(eOcupacao);
                        }

                        // Reativa a ocupação
                        this.da.reativarOcupacao(eOcupacao);

                        // Completa a transação
                        scope.Complete();
                    }
                    catch (Exception ex)
                    {
                        scope.Dispose();

                        throw ex;
                    }
                }
            }
            catch (BLException blEx)
            {
                throw blEx;
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }
        }

        /// <summary>
        /// Lista os motivo de ocupação ativos
        /// </summary>
        /// <returns>Lista de motivos de ocupação para combo</returns>
        public List<TOCombo> listarMotivosOcupacaoCombo()
        {
            List<TOCombo> retorno = null;

            try
            {
                retorno = da.listarMotivosOcupacaoCombo();
            }
            catch (BLException blex)
            {
                throw blex;
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }

        /// <summary>
        /// Lista os motivo de desocupação ativos
        /// </summary>
        /// <returns>Lista de motivos de desocupação para combo</returns>
        public List<TOCombo> listarMotivosDesocupacaoCombo()
        {
            List<TOCombo> retorno = null;

            try
            {
                retorno = da.listarMotivosDesocupacaoCombo();
            }
            catch (BLException blex)
            {
                throw blex;
            }
            catch (Exception ex)
            {
                throw new BLException(ex, true);
            }

            return retorno;
        }
    }
}
